#include "base_file_util.h"
#include "base_log.h"
#include <fstream>
#include <iostream>
#include <cstdio>
#include <cerrno>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <libgen.h>
#include <dirent.h>
#include <nlohmann/json.hpp>

namespace El {
namespace Base {

bool LoadBufferFromFile(const std::string &filePath, std::vector<uint8_t> &content)
{
    const int MAX_FILE_LENGTH = 32 * 1024 * 1024;
    std::ifstream file;
    file.open(filePath.c_str(), std::ios::in | std::ios::binary);
    if (!file.is_open()) {
        LOG_ERROR("open file failed! filePath:{}", filePath);
        return false;
    }

    file.seekg(0, std::ios::end);
    const long fileLength = file.tellg();
    if (fileLength > MAX_FILE_LENGTH) {
        LOG_ERROR("invalid file length({})!", fileLength);
        return false;
    }

    if (fileLength == 0) {
        content.clear();
        return true;
    }

    file.seekg(0, std::ios::beg);
    if (file.fail()) {
        LOG_ERROR("seekg file to begin failed! filePath:{}", filePath);
        return false;
    }

    content.resize(fileLength);
    file.read((char *)&content[0], fileLength);
    return true;
}

bool SaveBufferToFile(const std::string &filePath, const std::vector<uint8_t> &content, bool truncated /*= true*/)
{
    if (content.empty()) {
        LOG_WARN("content is empty, no need to save!");
        return true;
    }

    // if the file is not exist, create it first!
    std::ofstream file;
    if (truncated) {
        file.open(filePath.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
    } else {
        file.open(filePath.c_str(), std::ios::out | std::ios::binary | std::ios::app);
    }

    if (!file.is_open()) {
        LOG_ERROR("open file failed! filePath:{}", filePath);
        return false;
    }

    file.write((char *)&content[0], content.size());
    if (file.fail()) {
        LOG_ERROR("write content to file failed! file:{}", filePath);
        return false;
    }
    return true;
}

std::string ExtractFilePath(const std::string &fileFullName)
{
    return std::string(fileFullName).substr(0, fileFullName.rfind("/") + 1);
}

std::string ExtractFileName(const std::string &fileFullName)
{
    return std::string(fileFullName).substr(fileFullName.rfind("/") + 1, fileFullName.size());
}

void SyncFile(const std::string &dst)
{
    int fd = open(dst.c_str(), O_RDWR);
    if (fd < 0) {
        LOG_ERROR("open {} failed! err {}", dst, strerror(errno));
        return;
    }
    fsync(fd);
    close(fd);
}

bool CopyFile(const std::string &src, const std::string &dest, bool isAppend)
{
    char realPath[PATH_MAX + 1] = {0};
    if (realpath(src.c_str(), realPath) == nullptr) {
        LOG_ERROR("{} get realpath fail", src);
        return false;
    }

    std::ios_base::openmode mode = isAppend ? std::ios::app | std::ios::out : std::ios_base::out;
    std::ifstream fin(realPath);
    std::ofstream fout(dest, mode);
    if (!fin.is_open() || !fout.is_open()) {
        return false;
    }

    fout << fin.rdbuf();
    if (fout.fail()) {
        fout.clear();
        return false;
    }
    fout.flush();
    fout.close();
    SyncFile(dest); // no way to get fd from ofstream, so reopen to sync this file
    return true;
}

bool CreateDirectories(const std::string &path)
{
    if (path.empty()) {
        return false;
    }

    // 已存在则直接返回成功
    struct stat st;
    if (stat(path.c_str(), &st) == 0) {
        return S_ISDIR(st.st_mode);
    }

    // 递归创建父目录
    size_t pos = path.find_last_of('/');
    if (pos != std::string::npos) {
        std::string parent = path.substr(0, pos);
        if (!parent.empty() && !CreateDirectories(parent)) {
            return false;
        }
    }

    // 创建当前目录
    if (mkdir(path.c_str(), 0755) != 0 && errno != EEXIST) {
        LOG_ERROR("create directory {} failed: {}", path, strerror(errno));
        return false;
    }

    return true;
}

bool IsFileExists(const std::string &path)
{
    struct stat st;
    return (stat(path.c_str(), &st) == 0);
}

bool SaveFileWithBackup(const std::string &filePath, const std::vector<uint8_t> &content)
{
    // 1. 写入主文件
    if (!SaveBufferToFile(filePath, content, true)) {
        LOG_ERROR("Failed to write main file: {}", filePath);
        return false;
    }

    // 同步主文件
    SyncFile(filePath);

    // 2. 写入备份文件 - 使用隐藏文件形式
    std::string fileName = ExtractFileName(filePath);
    std::string filePath2 = ExtractFilePath(filePath);
    std::string backupPath = filePath2 + "." + fileName;

    if (!SaveBufferToFile(backupPath, content, true)) {
        LOG_WARN("Failed to write backup file: {}", backupPath);
        // 继续执行，因为主文件已经写入成功
    } else {
        // 同步备份文件
        SyncFile(backupPath);
    }

    return true;
}

bool LoadFileWithBackup(const std::string &filePath, std::vector<uint8_t> &content)
{
    // 1. 尝试读取主文件
    if (LoadBufferFromFile(filePath, content)) {
        // 验证内容是否为有效的JSON
        try {
            if (!content.empty()) {
                std::string jsonStr(content.begin(), content.end());
                auto json = nlohmann::json::parse(jsonStr);
                return true; // JSON有效，返回成功
            }
        } catch (const nlohmann::json::exception &e) {
            LOG_WARN("Main file JSON validation failed: {}", e.what());
            // JSON无效，继续尝试读取备份文件
        }
    }

    // 2. 尝试读取备份文件 - 使用隐藏文件形式
    std::string fileName = ExtractFileName(filePath);
    std::string filePath2 = ExtractFilePath(filePath);
    std::string backupPath = filePath2 + "." + fileName;

    if (IsFileExists(backupPath)) {
        if (LoadBufferFromFile(backupPath, content)) {
            LOG_INFO("Recovered file from backup: {}", backupPath);
            // 将备份文件恢复到主文件
            if (SaveBufferToFile(filePath, content, true)) {
                SyncFile(filePath);
                return true;
            }
        }
    }

    LOG_ERROR("Failed to load file with backup: {}", filePath);
    return false;
}

bool IsFileInUse(const std::string &filePath)
{
    // 尝试以独占方式打开文件
    int fd = open(filePath.c_str(), O_RDONLY | O_EXCL);
    if (fd == -1) {
        if (errno == EBUSY || errno == ETXTBSY) {
            // 文件被其他进程使用
            return true;
        }
        // 其他错误（如文件不存在）
        return false;
    }

    // 文件可以打开，说明没有被使用
    close(fd);
    return false;
}

bool IsDirectoryExists(const std::string &path)
{
    struct stat st;
    if (stat(path.c_str(), &st) != 0) {
        if (errno != ENOENT) { // 如果错误不是"文件不存在"，记录错误
            LOG_ERROR("Failed to check directory {}: {}", path, strerror(errno));
        }
        return false;
    }
    return S_ISDIR(st.st_mode);
}

bool TraverseDirectory(const std::string &path, const std::function<bool(const std::string &)> &callback)
{
    DIR *dir = opendir(path.c_str());
    if (dir == nullptr) {
        LOG_ERROR("Failed to open directory {}: {}", path, strerror(errno));
        return false;
    }

    bool result = true;
    struct dirent *entry;
    errno = 0; // 重置errno以检测readdir错误

    while ((entry = readdir(dir)) != nullptr) {
        // 跳过 "." 和 ".." 目录
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 调用回调函数处理文件
        if (!callback(entry->d_name)) {
            result = false;
            break;
        }
    }

    // 检查是否因为错误而退出循环
    if (errno != 0 && !result) {
        LOG_ERROR("Error while reading directory {}: {}", path, strerror(errno));
        result = false;
    }

    closedir(dir);
    return result;
}

} // namespace Base
} // namespace El